#include"lars_reactor.h"
#include"mysql.h"
#include"dns_route.h"
#include"lars.pb.h"
#include"subscribe.h"

tcp_server *server;

// 表示当前client已经订阅的mod集合
typedef __gnu_cxx::hash_set<uint64_t> client_sub_mod_list;


// 创建连接的HOOK回调函数
void create_subscribe(net_connection *conn, void *args){
    // 给当前conn绑定一个订阅mod的set集合
    conn->param = new client_sub_mod_list;
}
// 销毁连接的HOOK回调函数
void clear_subscribe(net_connection *conn, void *args){
    client_sub_mod_list* sub_list = (client_sub_mod_list *)(conn->param);
    
    // 去除集合中的所有数据
    client_sub_mod_list::iterator it;
    for(it=sub_list->begin(); it!=sub_list->end(); it++){
        // 依次取消dns的订阅
        SubscribeList::instance()->unsubscribe(*it, conn->get_fd());
    }
    
    delete sub_list;

    conn->param = NULL;
}

// 针对获取host的回调函数
void get_route(const char *data, uint32_t len, int msgid, net_connection* net_conn, void *args){
    // 1.解析proto文件
    lars::GetRouteRequest requst;
    requst.ParseFromArray(data, len);

    // 2.得到请求的modid和cmdid
    int modId = requst.modid();
    int cmdId = requst.cmdid();

    // 2.5如果之前没有订阅该modid和cmdid，则订阅当前mod
    uint64_t mod = (((uint64_t)modId)<<32) + cmdId;
    
    client_sub_mod_list* sub_list = (client_sub_mod_list *)(net_conn->param);
    if(sub_list == NULL){
        fprintf(stderr, "sub_list is NULL!\n");
    }
    else if(sub_list->find(mod) == sub_list->end()){
        // 将当前mod加入到集合
        sub_list->insert(mod);

        // 订阅当前mod
        SubscribeList::instance()->subscribe(mod, net_conn->get_fd());
        printf("fd:[%d] subscribe modid:[%d], cmdid:[%d]\n",net_conn->get_fd(), modId, cmdId);
    }
    else{
        printf("this set have the mod:%lu\n", mod);
    }

    // 3.通过modid和cmdid获取对应的主机的IP和port
    host_set hosts = Route::instance()->get_hosts(modId, cmdId);

    // 4.将结果打包成proto数据包
    lars::GetRouteResponse response;
    response.set_modid(modId);
    response.set_cmdid(cmdId);
    
    for(host_set_it it = hosts.begin(); it!=hosts.end(); it++){
        // 此时的it就是ip+port组成的uint64_t数据
        uint32_t ip = (uint32_t)((*it)>>32);
        uint32_t port = (uint32_t)(*it);
        
        lars::HostInfo host;
        host.set_ip(ip);
        host.set_port(port);

        // 将host添加到response中
        response.add_host()->CopyFrom(host);
    }

    // 将response转换成字符串
    std::string responseString;
    response.SerializeToString(&responseString);

    // 5.将打包好的proto数据发送回去
    net_conn->send_message(responseString.c_str(), responseString.size(), lars::ID_GetRouteResponse);

}

int main(int argc, char** argv){
    event_loop loop;

    // 加载配置文件
    config_file::setPath("./conf/dns.conf");
    std::string ip = config_file::instance()->GetString("reactor", "ip", "0.0.0.0");
    short port = config_file::instance()->GetNumber("reactor", "port", 8888);
    printf("ip: %s\tport: %d\n", ip.c_str(), port);

    // 允许连接的最大数
    int maxConn = config_file::instance()->GetNumber("reactor", "maxConn", 100);
    // 线程池个数，建议配置个数和CPU个数一致
    int thread_cnt = config_file::instance()->GetNumber("reactor", "threadNum", 3);  
    printf("maxConn: %d\tthread_cnt: %d\n", maxConn, thread_cnt);
    
    //创建套接字
    server = new tcp_server(&loop, ip.c_str(), port, maxConn, thread_cnt);

    // 注册创建连接和销毁连接的HOOK函数
    server->set_conn_start(create_subscribe);
    server->set_conn_close(clear_subscribe);


    // 注册一个回调业务
    server->add_msg_router(lars::ID_GetRouteRequest, get_route);


    
    pthread_t tid;

    // 开辟一个线程，定期发布已经更改的mod的集合
    // int ret = pthread_create(&tid, NULL, publish_change_mod, NULL);
    
    // 开辟一个BackendThread线程实时监控版本信息
    int ret = pthread_create(&tid, NULL, check_route_change, NULL);

    if(ret != 0){
        perror("pthread_create error\n");
        exit(1);
    }
    // 设置线程分离
    pthread_detach(tid);

    // 进入循环开始监听
    loop.event_process();
    
    return 0;
}